/**
 * @file libreria.h
 *
 * @author Federico Consoli
 *
 * Released under GNU GPLv3 (http://www.gnu.org/licenses/gpl-3.0.txt)
 */

/**
 * @mainpage Service Oriented Architecture Documentation
 *
 * @section intro Service Oriented Architecture?
 * A simplified distributed Service Oriented Architecture system for POSIX compliant systems written in C++. 
 *
 * This project is intended for the course of Operating Systems And
 * Distributed Programming, held by prof. Giuseppe Lipari.
 *
 * @section howto How do I use this documentation?
 * Documentation provides a brief explanation on how to develop a Service Oriented Architecture
 *  using the library, and an analysis of overall implementation.
 *
 * Before starting, you should take a look at the project specifications
 * (doc/progetto-unix2010-11.pdf) and README.
 *
 * -> Start @link Service here @endlink 
 * 
 * Other interesting documentation:
 * -  @link lettori_scrittori lettori_scrittori @endlink problem 
 * and the class implementation @link Lettori_scrittori lettori scrittori class @endlink
 * - Utility class @link Connection connection @endlink
 */

#ifndef LIBRERIA_H
#define	LIBRERIA_H





// Number of thread for each service
#define MAXNTHREAD 5
// Max number of services 
#define MAXNSERVICES 10 
// Cast for the accept function 
#define CAST_ADDR(x) ( struct sockaddr *)(x)

#include <cstring>
#include <sstream>
#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <vector>
#include <fstream>
#include <stdio.h>
#include <cstdlib>
#include <errno.h>
#include <pthread.h>
#include <dirent.h>
#include <sys/stat.h> 
#include "connect_utility.h"
#include "lettori_scrittori.h"
#include "eccezioni.h"

using namespace std;

/** Implementation of the Service base class.
 * This class is the universal class for the server, register and client
 *
 */
class Service {
public:

        /** Structure to store service information if there are multiple services they are stored in a list*/
    struct servizio {
        /** Name of the service */
        string nome_servizio;
        /** Ip of the service*/
        string ip;
        /** Port of the service*/
        unsigned short port;
        /** List of input parameters separated by a space*/
        string parametri_in;
        /** List of output parameters separated by a space*/
        string parametri_out;
        /** Integer used for the priority of the scheduling of services, it's only 0 or 1 value*/
        int priority;
        /** Pointer to the next service */
        servizio* serv_succ;
    };
    /** Name of the process (Service provider name or client name or register name)*/
    string name;
    /** Head of the list of services*/
    servizio*testa_elenco_servizi;
protected:

    /** @link Lettori_scrittori Readers-writers @endlink class element's for multiple read and single write */
    Lettori_scrittori reg_sem;
    /** Ip of the Register */
    string ip_reg;
    /** Port of the register */
    unsigned short port_reg;


public:


    /**
     * Service Constructor. Initializes the port and ip of the service registry
     *
     * @param ip ip of the Register
     * @param port of the register
     */
    Service(string ip = "127.0.0.1", unsigned short port = 40000);


    /**
     * Service Destroyer. Delete all services stored. 
     */
    ~Service();
    /**
     * It creates a new service and places it in the list of services
     * @param nome Name of the service.
     * @param par_in List of input parameters.
     * @param par_out List of output parameters.
     * @param port Port of the service.
     * @param ip Ip of the service.
     * @return If the services does not exist the pointer to the 
     * struct @link Service::servizio servizio @endlink inserted else it will return NULL.
     */
    servizio* aggiungi_servizio(string, string, string, unsigned short, string);
    /**
     * Remove the service from the list of services.
     * @param nome Name of the service.
     * @param ip Ip of the service provider.
     * @param porta Port of the service provider.
     * @return 0
     */
    int rimuovi_servizio(string, string, unsigned short);
    /**
     * It lists the services that are stored.
     * @return 0 if the list is empty else 1.
     */
    int elenca_servizi();
    /**
     * It looks if a service is in the list
     * @param elem pointer to the struct @link Service::servizio servizio @endlink to looking for.
     * @return if a service of type elem is present it return pointer 
     * to the to the struct @link Service::servizio servizio@endlink. Else it return NULL.
     */
    servizio* cerca_servizio(servizio*);
    /**
     * Send a struct @link Service::servizio servizio @endlink to the socket descriptor sd
     * @param elem pointer to the struct @link Service::servizio servizio @endlink to send.
     * @param sd socket descriptor of the connection.
     * @return 0
     */
    int invia_servizio(servizio*, int);
    /**
     * Receive a struct @link Service::servizio servizio @endlink from the socket descriptor sd
     * @param sd socket descriptor of the connection.
     * @return pointer to the to the struct @link Service::servizio servizio@endlink received.
     */
    servizio *ricevi_servizio(int);
    /**
     * Create a new string with the date, time and the name of the server.
     * @return string
     */
    string display();


};

#ifdef REGISTRO

/** Implementation of the Register class.
 *  It is responsible for recording correspondence between service and service providers.
 */
class Registro : public Service {
    /**Semaphore for mutual exclusion of handling of connections from the client or provider of services*/
    pthread_mutex_t m_acc;

    /** Array of pthread handle */
    pthread_t tid[MAXNTHREAD];

    /** Structure containing values ​​to be passed to each thread */
    struct threadargs {
        /** This element (Registro) */
        Registro* elem;
        /** socket descriptor of listing for connection */
        int sd;
        /** Thread's number*/
        int num_thread;
    };
    /** Register information */
    threadargs reg_param;
public:

    /**
     * Register constructor. It initializes the socket for incoming 
     * connections and initializes the structure of arguments to the thread.
     * @param ip ip of the register
     * @param port of the register
     * @throw Exception_class  
     * - Exception Number 15: Unable to listen on the ip/port
     */
    Registro(string ip = "127.0.0.1", unsigned short port = 40000);

    /**
     * Register destroyer. Shutdown the socket for the incoming connection, 
     * waits that all threads end with success and destroy the mutex @link Registro::m_acc m_acc @endlink
     * @throw Exception_class  
     * - Exception Number 14: Error in the cancellation of the semaphore
     */
    ~Registro();

    /** It initializes and creates the thread. Show the Control menu.
     * Using the menu can or invoke the function @link Service::elenca_servizi elenca_servizi @endlink
     * @throw Exception_class
     * - Exception Number 9: Error in the initialization of the mutex.
     * - Exception Number 13: Error creating thread.
     */
    void avvia();

    /**
     * it searches for a service in the list and assigns it based on the scheduling policy.
     * Algorithm used is like "Second chance algorithm" scheduling.
     * @param nome Name of the service.
     * @param par_in List of input parameters.
     * @param par_out List of output parameters.
     * @return Pointer to the structure of type @link Service::servizio servizio@endlink. 
     * If there are no elements it returns null.
     */
    servizio* assegna_servizio(string, string, string);

    /**
     * It receives the service request from the @link Client Client@endlink, 
     * calls the @link Registro::assegna_servizio assegna_servizio@endlink  
     * and if it finds a service provider, it sends the service, with ip and 
     * port service providers, the to @link Client Client@endlink.
     * @param sd Socket descriptor of the connection to the @link Client client@endlink
     */
    void cerca_e_invia(int);

    /**
     * It identifies service providers and clients and manages their commands.
     * @param sd Socket descriptor of the connection (client or service provider).
     * @param client_id Identifier, it can be only client or server.
     */
    void identifica_e_gestisci(int, string);

    /**
     * Remove a service provider @link Service::servizio servizio@endlink from the list.
     * @param sd Socket descriptor of the connection to the service provider.
     * @return 0 if the service was present and was successfully removed otherwise it return 1 
     * if it was not present.
     */
    int rimuovi_servizio(int);

    /**
     * Thread body function. It start the thread after the @link avvia() avvia @endlink function.
     * @param arg Pointer to the @link Registro::reg_param reg_param@endlink struct
     * @return NULL
     */
    static void *body(void *);
};
#endif

#ifdef CLIENT

/** Implementation of the Client class.
 *  It is a generic class for the Client.
 */
class Client : public Service {
public:

    /**
     * It makes a request of a @link Service::servizio servizio@endlink to the register
     * @param req_serv It's a @link Service::servizio servizio@endlink without ip and port
     * @throw Exception_class
     * - Exception Number 16: Unable to connect to services @link Registro register @endlink
     * @return 1 if the service is found in the register else 0.
     */
    int richiedi_servizio(servizio*);
    /**
     * Constructor of client
     * @param ip Ip of @link Registro register @endlink
     * @param port Port of @link Registro register @endlink
     * @param client_name Name of the @link Client client @endlink
     */
    Client(string client_name = "CLIENT", string ip="127.0.0.1", unsigned short port=40000 );


    /**
     * Change client name
     * @param name_of_client Name of the @link Client client @endlink
     */
    void change_client_name(string);
};
#endif

/** Implementation of the Responce class.
 *  Response class is responsible for encoding the response. 
 * It has methods to encode and transmit the output parameters 
 * (by the service provider) and to receive and decode (client side).
 */
class Response : public Service {
    //In ogni array sono presenti il numero dei parametri in ingresso
    //Per ogni tipologia
    // array[0] numero di int
    // array[1] numero di string
    // array[2] numero di double
    // array[3] numero di buffer

    /** Number of type of parameters, default 4 : int string double buffer */
    int num_param;
    /** It is an array that contains for each input type parameter the number of it */
    int *array_in;
    /** It is an array that contains for each output type parameter the number of it */
    int *array_out;

    /**
     * @link Response::crea_resp crea_resp @endlink function utility.
     * @param parametri_in List of input parameter.
     * @param parametri_out List of output parameter.
     * 
     * * @throw Exception_class
     * - Exception Number 3: Error in the creation of the input variables.
     * - Exception Number 4: Error in the creation of the output variables.
     * @return 0
     */
    int crea_variabili(vector<string>&, vector<string>&);




public:

    /** It is a generic structure that contains the input parameters or output parameters. */
    struct valori {
        /** Array of int value. */
        int *array;
        /** Array of string value.*/
        string *str;
        /** Array of double value. */
        double *dou;
        /** Array of buffer value. */
        char **buffer;
        /** Array that contains the length of each buffer value. */
        int *bufflen;

    };

    /**
     * Response Constructor. It set @link Response::num_param num_param @endlink and 
     * initializes the number of @link Response::array_in input parameters @endlink and 
     * the number of @link Response::array_out output parameters @endlink
     * @param name_se Name of Response, default "RESPONSE"
     * 
     */
    Response(string name_se = "RESPONSE");
    /**
     * Response Constructor. It set @link Response::num_param num_param @endlink and 
     * initializes the number of @link Response::array_in input parameters @endlink and 
     * the number of @link Response::array_out output parameters @endlink
     * @param ip Ip of @link Registro register @endlink
     * @param port Port of @link Registro register @endlink
     * 
     */
    Response(string, unsigned short);

    /**
     * Destroyer. It delete @link Response::array_in input parameters @endlink and 
     * @link Response::array_out output parameters @endlink
     */
    ~Response();

    /**
     * It receives a @link Service::servizio servizio@endlink request from
     *  the client and check if the parameters are appropriate
     * @param sd Socket descriptor of the connection to the @link Client client@endlink.
     * @return 1 If match else 0.
     */
    int confronto(int);

    /**
     * Build a standard responce for the @link Service::servizio servizio@endlink
     * It dimensioning the @link Response::array_in array_in @endlink and
     *  @link Response::array_out array_out @endlink values.
     * @param crea_ser @link Service::servizio Servizio@endlink pointer 
     * of which the responce must be created.
     * @throw Exception_class
     * - Exception Number 2: service already present
     * @return 0
     */
    int crea_resp(servizio*);

    /**
     * It receive a @link Response::valori valori@endlink struct
     * @param sd Socket descriptor of the connection to the @link Client client@endlink.
     * @param param String that identify if it's an out packet or in packet
     * @throw Exception_class
     * - Exception Number 5: Error in receiving @link Response::valori valori@endlink struct
     * @return @link Response::valori valori@endlink struct
     */
    valori * ricevi_pacchetto(int, string);

    /**
     * Build an empty @link Response::valori valori@endlink struct of type param.
     * @param param String that identify if it's an out packet or in packet
     * @throw Exception_class
     * - Exception Number 6: Error in Build @link Response::valori valori@endlink struct
     * @return @link Response::valori valori@endlink struct
     */
    valori *pacchetto_vuoto(string);

    /**
     * It send a @link Response::valori valori@endlink struct
     * @param pacchetto_out @link Response::valori valori@endlink struct to send.
     * @param sd Socket descriptor of the connection to the @link Client client@endlink.
     * @param param String that identify if it's an out packet or in packet
     * @throw Exception_class
     * - Exception Number 7: Error in send @link Response::valori valori@endlink struct
     * @return 0
     */
    int invia_pacchetto(valori*, int, string);

    /**
     * Delete a @link Response::valori valori@endlink struct
     * @param pacchetto @link Response::valori valori@endlink struct to delete
     * @param tipo String that identify if it's an out packet or in packet
     * @throw Exception_class
     * - Exception Number 8: Error in deleting @link Response::valori valori@endlink struct
     * @return 0
     */
    int rem_val(valori*&, string);
};


#ifdef SERVER

/** Implementation of the Server class.
 *  Server class is generic class for service provider
 */
class Server : public Service {
    /** Array of mutex, one mutex for 
     * each @link Service::servizio Servizio@endlink accept */
    pthread_mutex_t m_acc[MAXNSERVICES];
    /** Matrix of pthread handle, MAXNTHREAD is the number of thread
     * for every service and  MAXNSERVICES is the number of the service */
    pthread_t tid[MAXNSERVICES][MAXNTHREAD];

    /** Index for the number of services*/
    int indice_sem_servizio;

    /** Structure containing values ​​to be passed to each thread */
    struct threadarg {
        /** This */
        Server *elem;
        /** Socket descriptor of the service */
        int sd;
        /** Responce class of the service*/
        Response *R;
        /** Index of the @link Server::m_acc m_acc @endlink array */
        int indice_sem;
        /** Thread number of the service */
        int num_thread;
    };
    /**
     * Thread body function. For every services is build only 1 @link Response Response @endlink .
     * Every thread listen in mutual exclusion on the socket of the services and after a connection
     * it call the @link Server::scegli scegli @endlink function.
     * @param arg Pointer to the @link Server::threadarg threadarg @endlink structure 
     * @return NULL
     */
    static void *body(void *);

    /** It contains the addresses of the @link Server::threadarg threadarg @endlink. 
     * They will then be destroyed at the end */
    threadarg **elenco_resp;

public:

    /**
     * Constructor. It initialize @link Server::m_acc m_acc @endlink array,
     *  @link Server::tid tid@endlink array and @link Server::indice_sem_servizio @endlink .
     */
    //Server();

    /**
     * Constructor. It initialize @link Server::m_acc m_acc @endlink array,
     *  @link Server::tid tid@endlink array and @link Server::indice_sem_servizio @endlink .
     * It also can set the ip and port of Register
     * @param ip Ip of @link Registro register @endlink
     * @param port Port of @link Registro register @endlink
     */
    Server(string ip="127.0.0.1", unsigned short port=40000);

    /**
     * Destroyer. 
     * - Close all socket service.
     * - Wait that all threads for the services return.
     * - Delete the @link Response Response @endlink and  @link Server::threadarg threadarg @endlink. 
     * - Delete all mutex.
     */
    ~Server();

    /**
     * This function must be definited in each services provider.
     * @param sd Socket descriptor of the connection to the @link Client client@endlink.
     * @param R Pointer to the  @link Response @endlink element.
     */
    void scegli(int sd, Response * R);

    /**
     * Unregister all the service from the Registro.
     * @return 0
     */
    int deregistrati();

    /**
     * Registers a service at the Registro
     * @param nome Name of the service.
     * @param par_in List of input parameters.
     * @param par_out List of output parameters.
     * @param port Port of the service.
     * @param ip Ip of the service.
     * @throw Exception_class
     * - Exception Number 10: Error Unable to connect to the Registro
     * @return 0
     */
    int registra_servizio(string, string, string, string, unsigned short);

    /**
     * Unregister a service from the Registro.
     * @param nome Name of the service.
     * @param ip Ip of the service.
     * @param port Port of the service.
     * @throw Exception_class
     * - Exception Number 11: Error in the cancellation of the service, trying 
     * to delete a @link Service::servizio servizio @endlink not present
     * - Exception Number 22: Error not recognized from Registro
     * @return 0
     */
    int deregistra_servizio(string, string, unsigned short);

    /**
     * It looks if a @link Service::servizio servizio @endlink is in the local list.
     * @param nome Name of the service.
     * @param ip Ip of the service.
     * @param port Port of the service.
     * @return pointer to the @link Service::servizio servizio @endlink struct, 
     * NULL if the @link Service::servizio servizio @endlink is not present.
     */
    servizio* cerca_servizio(string, string, unsigned short);

    /**
     * Start a service:
     * - Build the Response for the @link Service::servizio servizio @endlink
     * - Create the socket for the @link Service::servizio servizio @endlink
     * - Create the @link Server::threadarg threadarg @endlink to pass to the thread
     * - Create the thread for the @link Service::servizio servizio @endlink
     * @throw Exception_class
     * - Exception Number 1: Error creating socket
     * - Exception Number 13: Error creating thread
     * @param nuovo Pointer to the @link Service::servizio servizio @endlink
     * struct to start
     */
    void avvia_servizio(servizio*);

};

#endif




#endif	/* LIBRERIA_H */